`include "defines.svh"
`include "CP0Defines.svh"
module ALU(
    input logic [31:0]A,
    input logic [31:0]B,
    input logic [4:0]EXE_ALUOp,
    input ExceptionType EXE_ExceptionType,
    output logic [31:0]ALU_Out,
    output ExceptionType EXE_ExceptionTypeEnd
);
    logic [4:0]d;    //移位
    assign d = A[4:0];

    always_comb begin
        begin
            unique case(EXE_ALUOp)
            `ALU_add:
                ALU_Out = A+B;
            `ALU_addu:
                ALU_Out = A+B;
            `ALU_sub:
                ALU_Out = A-B;
            `ALU_subu:
                ALU_Out = A-B;
            `ALU_sll:
                ALU_Out = B<<d;
            `ALU_srl:
                ALU_Out = B>>d;
            `ALU_sra:
                ALU_Out = $signed($signed(B)>>>d);
            `ALU_and:
                ALU_Out = A&B;
            `ALU_or:
                ALU_Out = A|B;
            `ALU_xor: 
                ALU_Out = A^B;
            `ALU_nor:
                ALU_Out = ~(A|B);
            `ALU_slt:begin
                if($signed(A)<$signed(B))
                    ALU_Out = {31'b0,1'b1};
                else
                    ALU_Out = 32'b0; 
            end
            `ALU_sltu:begin
                if($unsigned(A)<$unsigned(B))
                    ALU_Out = {31'b0,1'b1};
                else
                    ALU_Out = 32'b0; 
            end
            `ALU_A:
                ALU_Out = A;
            `ALU_B: 
                ALU_Out = B;
            default:
                ALU_Out = 32'b0;
            endcase
        end
    end

    assign EXE_ExceptionTypeEnd.WrongAddrInIF=EXE_ExceptionType.WrongAddrInIF;
    assign EXE_ExceptionTypeEnd.WrongAddrInDataWr=EXE_ExceptionType.WrongAddrInDataWr;
    assign EXE_ExceptionTypeEnd.WrongAddrInDataRd=EXE_ExceptionType.WrongAddrInDataRd;
    assign EXE_ExceptionTypeEnd.Overflow=((EXE_ALUOp==`ALU_add)&&(A[31]==B[31])&&(A[31]!=ALU_Out[31]))||((EXE_ALUOp==`ALU_sub)&&(A[31]!=B[31])&&(A[31]!=ALU_Out[31]));
    assign EXE_ExceptionTypeEnd.Syscall=EXE_ExceptionType.Syscall;
    assign EXE_ExceptionTypeEnd.Break=EXE_ExceptionType.Break;
    assign EXE_ExceptionTypeEnd.ReservedInstr=EXE_ExceptionType.ReservedInstr;
    assign EXE_ExceptionTypeEnd.Interrupt=EXE_ExceptionType.Interrupt;
    assign EXE_ExceptionTypeEnd.Eret=EXE_ExceptionType.Eret;
endmodule